﻿using MoXAML.Infrastructure;
using System.Text;
using EnvDTE;
using Microsoft.VisualBasic.CompilerServices;
using System;
using System.IO;

namespace MoXAML.PowerToys.Classic
{
    public class NotifyPropertyCommand : CommandBase
    {
        /// <summary>
        /// Initializes the comment.
        /// </summary>
        public NotifyPropertyCommand()
            : base()
        {
            this.CommandName = "CommandNotifyProperty";
            this.Caption = "Make Notify Property";
            ParentCommandBar.Add(CommandBarType.CodeContextMenu);
        }

        /// <summary>
        /// Amend the property so that it's a notify property.
        /// </summary>
        public override void Execute()
        {
            CodeProperty element = (CodeProperty)this.GetCodeElementAtTextPoint(vsCMElement.vsCMElementProperty);
            Type tp = element.GetType();
            TextSelection sel = (TextSelection)Application.ActiveDocument.Selection;
            sel.MoveToPoint(element.StartPoint, false);
            sel.MoveToPoint(element.EndPoint, true);

            // Now, deselect the selection.
            string name = BuildProperty(element);

            string changed = BuildChanged();
            FileCodeModel fcm = Application.ActiveDocument.ProjectItem.FileCodeModel;
            bool found = false;
            if (fcm != null)
            {
                foreach (CodeElement ce in fcm.CodeElements)
                {
                    if (ce.Kind == vsCMElement.vsCMElementNamespace)
                    {
                        foreach (CodeElement names in ce.Children)
                        {
                            if (names.Kind == vsCMElement.vsCMElementClass)
                            {
                                foreach (CodeElement child in names.Children)
                                {
                                    if (child.Kind == vsCMElement.vsCMElementFunction && child.Name == "NotifyPropertyChanged")
                                    {
                                        found = true;
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            if (!found)
            {
                name = changed + "\r\n\r\n" + name;
            }
            //_application.ActiveDocument

            sel.ReplaceText(sel.Text, name, (int)vsFindOptions.vsFindOptionsNone);
            sel.SmartFormat();
            sel.MoveToPoint(sel.TopPoint, false);
        }

        private string BuildChanged()
        {
            if (FileIsCSharp)
            {
                return "protected virtual void NotifyPropertyChanged(string propertyName)\r\n{\r\nPropertyChangedEventHandler handler = PropertyChanged;\r\nif (handler != null)\r\n{\r\nhandler(this, new PropertyChangedEventArgs(propertyName));\r\n}\r\n}";
            }
            else
            {
                return "Protected Overridable Sub NotifyPropertyChanged(propertyName As String)\r\nIf PropertyChanged <> Nothing Then\r\nPropertyChanged(Me, New PropertyChangedEventArgs(propertyName))\r\nEnd If\r\n\r\nEnd Sub";
            }
        }


        /// <summary>
        /// Build the property text.
        /// </summary>
        /// <param name="property">The property the cursor is on.</param>
        /// <returns>The populated property.</returns>
        private string BuildProperty(CodeProperty property)
        {
            string propName = property.Name;
            string fieldName = string.Empty;
            if (property.Name.Length == 1)
                fieldName = property.Name;
            else
                fieldName = property.Name.Substring(0, 1).ToLower() + property.Name.Substring(1);
            StringBuilder text = new StringBuilder();
            try
            {
                if (FileIsCSharp)
                {
                    string t = "private " + property.Type.AsString + " _" + fieldName + ";\r\n";
                    t += "public " + property.Type.AsString + " " + propName + "\r\n{\r\n";
                    text.Append(t);
                    text.Append("get\r\n{\r\nreturn _" + fieldName + ";\r\n}\r\n");
                    text.Append("set\r\n{\r\nif (_" + fieldName + " != value)\r\n{");
                    text.Append("_" + fieldName + " = value;\r\n");
                    text.Append("NotifyPropertyChanged(\"" + propName + "\");\r\n");
                    text.Append("}\r\n}\r\n}");
                }
                else
                {
                    string t = "Private _" + fieldName + " As " + property.Type.AsString + "\r\n";
                    t += "Public Property " + fieldName + " As " + property.Type.AsString + "\r\n";
                    text.Append(t);
                    text.Append("Get\r\nReturn _" + fieldName + "\r\nEnd Get\r\n");
                    text.Append("Set (ByVal Value As " + property.Type.AsString + ")\r\nIf _" + fieldName + " <> Value Then\r\n");
                    text.Append("_" + fieldName + " = Value\r\nNotifyPropertyChanged(\"" + fieldName + "\")\r\nEnd If\r\nEnd Set\r\nEnd Property\r\n");
                }
            }
            catch (Exception ex)
            {
                ProjectData.SetProjectError(ex);
                ProjectData.ClearProjectError();
            }

            return text.ToString();
        }

        private bool FileIsCSharp
        {
            get { return Path.GetExtension(this.Application.ActiveDocument.FullName).ToLower() == ".cs"; }
        }

    }
}
